#include "NodeManager.h"

#include <string.h>	// declares NULL
#include <stdio.h> // printf

#ifdef _WIN32
#ifdef _DEBUG
   #include <crtdbg.h>
   #undef THIS_FILE
   static char THIS_FILE[] = __FILE__;
   #define new       new( _NORMAL_BLOCK, __FILE__, __LINE__)
   #define malloc(s) _malloc_dbg(s, _NORMAL_BLOCK, __FILE__, __LINE__)
#endif
#endif

// Constructor
NodeManager::NodeManager() {
	// blank out link list of nodes
	this->head = NULL;
}

// Deconstructor
NodeManager::~NodeManager() {
	Node *slider = this->head;
	Node *tmp;

	// loop through nodes freeing each one in turn
	while(slider != NULL) {
		tmp = slider;
		slider = slider->next;

		FreeLibrary(tmp->hLibrary);
		delete tmp;
	}
}

// add a new node
Node* NodeManager::add(char *dll) {
	// These three must match what is defined in NodeManager.h
	typedef ParamManager* (*load)(void);
	typedef void (*unload)(void);
	typedef void (*start)(HANDLE /*mutex*/, ParamManager& /*detail*/, char* /*index*/);


	// set initial values
	Node *slider = this->head;

	// this will be the first node
	if(slider == NULL) {
		this->head = new Node;
		this->head->prev = NULL;
		this->head->next = NULL;

		slider = this->head;
	}
	// not the first node, add to end of list
	else {
		// advanced till slider is the last node
		while(slider->next != NULL) {
			slider = slider->next;
		}
		// at end of list, attach new node
		slider->next = new Node;
		slider->next->prev = slider;
		slider->next->next = NULL;

		slider = slider->next;
	}

	bool result = true;

	slider->hLibrary = LoadLibraryA(dll);
	if(slider->hLibrary) {
		// load up function calls within library
		slider->_load = (load)GetProcAddress(slider->hLibrary, "load");
		slider->_unload = (unload)GetProcAddress(slider->hLibrary, "unload");
		slider->_start = (start)GetProcAddress(slider->hLibrary, "start");

		if(!slider->_load) {
			printf("_load failed\n");
			// failed to load function from thread
			result = false;
		}
		if(!slider->_unload) {
			printf("_unload failed\n");
			// failed to load function from thread
			result = false;
		}
		if(!slider->_start) {
			printf("_start failed\n");
			// failed to load function from thread
			result = false;
		}
	}
	else {
		printf("...else failed\n");
		result = false;
	}

	if(!result) {
		printf("DLL Failed To Load!\r\n");
		printf("GetLastError(%d)\r\n", GetLastError());
		delete slider;
		return NULL;
	}

	// remember multiple 'supported' actions
	slider->pm.setOverwrite(false);

	// load up supported actions, this calls the one-time 'load' function
	ParamManager *supported = slider->_load();
	supported->reset();
	while(Param *param = supported->getNext()) {
		if(!strcmp(param->data, "action")) {
			slider->pm.set("supported", param->value);
		}
	}

	// return pointer to new node
	return slider;
}

// del a node by the specified id
bool NodeManager::del(Node *node) {
	Node *slider = this->head;

	if(slider == NULL) {
		return false;
	}

	while(slider != NULL) {
		if(slider == node) {
			break;
		}
		slider = slider->next;
	}

	if(slider == NULL) {
		return false;
	}

	if(slider == this->head) {
		this->head = slider->next;
		if(this->head != NULL) {
			this->head->prev = NULL;
		}
	}
	else {
		if(slider->prev != NULL) {
			slider->prev->next = slider->next;
		}
		if(slider->next != NULL) {
			slider->next->prev = slider->prev;
		}
	}

	// call one-time unload function within module
	slider->_unload();

	// unload library module
	FreeLibrary(slider->hLibrary);
	delete slider;

	return true;
}

// get a node by the specified id
Node* NodeManager::get(char *todo) {
	Node *slider = this->head;
	char process[DEFAULT_BUFLEN];
	char delim[2] = ";";
//	char *pTok;

	// loop through each node
	while(slider != NULL) {
		// a temporary string to use with strtok so the original is left ok
		strcpy_s(process, DEFAULT_BUFLEN, slider->pm.get("supported"));

/*		// get the next parameter
		pTok = strtok(process, delim);
		while(pTok != NULL) {
//			printf("[NodeManager::get, looking for '%s', next provided todo '%s'\r\n", todo, pTok);
			if(!strcmp(pTok, todo)) {
				return slider;
			}

			// pop off the next parameter
			pTok = strtok(NULL, delim);
		}
*/
		// check if this node supports the specified action
		if(slider->pm.isMatch("supported", todo)) {
			return slider;
		}

		// advanced till slider to next node
		slider = slider->next;
	}

	return NULL;
}

// load available node modules
bool NodeManager::load() {
	Node *node;

	WIN32_FIND_DATA ffd;
	TCHAR szDir[MAX_PATH];
	HANDLE hFind = INVALID_HANDLE_VALUE;
	DWORD dwError=0;
	char data[DEFAULT_BUFLEN];

	// prepare safe string directory path
	StringCchCopy(szDir, MAX_PATH, "nodes");
	StringCchCat(szDir, MAX_PATH, TEXT("\\*"));

	// Find the first file in the directory.
	hFind = FindFirstFile(szDir, &ffd);
	if(INVALID_HANDLE_VALUE == hFind) {
		return false;
	} 
   
	// Loop through file directory listing, loading nodes
	do {
		// Is this a directory?
		if(ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
			// do nothing
			//_tprintf(TEXT("  %s   <DIR>\n"), ffd.cFileName);
		}
		else {
			// load up node dll
			if(strstr(ffd.cFileName, "dll")) {
				sprintf(data, "nodes\\%s", ffd.cFileName);
				node = this->add(data);
				if(node) {
#ifdef _DEBUG
					printf("Loading node: %s\n", node->pm.get("supported"));
#endif // _DEBUG
				}
			}
		}
	}	while(FindNextFile(hFind, &ffd) != 0);
 
	dwError = GetLastError();
	if(dwError != ERROR_NO_MORE_FILES)  {
		return false;
	}

	FindClose(hFind);
	return true;
}